// pages/promise/simple/simple.js


// https://m.runoob.com/js/js-promise.html
Page({

  /**
   * 页面的初始数据
   */
  data: {

  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad(options) {

  },

  /**
   * 生命周期函数--监听页面初次渲染完成
   */
  onReady() {

  },

  /**
   * 生命周期函数--监听页面显示
   */
  onShow() {

  },

  /**
   * 分时间执行3个任务，每个任务间隔2s
   * 用 "函数瀑布" 来实现的。可想而知，在一个复杂的程序当中，用 "函数瀑布"（碎成） 实现的程序无论是维护还是异常处理都是一件特别繁琐的事情，而且会让缩进格式变得非常冗赘。
   */
  noPromise() {
    setTimeout(() => {
      console.log(new Date(), '---执行任务1')
      setTimeout(() => {
        console.log(new Date(), '---执行任务2')
        setTimeout(() => {
          console.log(new Date(), '---执行任务3')
        }, 2000)
      }, 2000)
    }, 2000);
  },

  usePromise() {
    new Promise(function (resole, reject) {
      console.log(new Date(), '---耗时任务1')
      // 耗时任务1
      setTimeout(() => {
        const random = Math.floor(Math.random() * 3)
        if (random == 0) {
          console.log(new Date(), '---耗时任务1 成功 next', random)
          resole('成功');
        } else {
          console.log(new Date(), '---耗时任务1 失败 next', random)
          reject('失败')
        }
      }, 2000);
    }).then(
      (res) => {
        return new Promise((resle, reject) => {
          console.log(new Date(), '---耗时任务2，任务1 ', res)
          setTimeout(() => {
            const random = Math.floor(Math.random() * 3)
            if (random == 0) {
              console.log(new Date(), '---耗时任务2 成功 next ', random)
              resle('成功');
            } else {
              console.log(new Date(), '---耗时任务2 失败 next', random)
              reject('失败')
            }
          }, 2000);
        })
      },
      function (fail) {
        console.log(new Date(), '---耗时任务2，任务1(2)', fail)
      }).then(
        (success) => {
          console.log(new Date(), '---耗时任务3，任务2', success)
        },
        (fail) => {
          console.log(new Date(), '---耗时任务3，任务3(2)', fail)
        },
      )
  },

  /**
   *  Promise 的构造函数
Promise 构造函数是 JavaScript 中用于创建 Promise 对象的内置构造函数。
Promise 构造函数接受一个函数作为参数，该函数是同步的并且会被立即执行，所以我们称之为起始函数。起始函数包含两个参数 resolve 和 reject，分别表示 Promise 成功和失败的状态。
起始函数执行成功时，它应该调用 resolve 函数并传递成功的结果。当起始函数执行失败时，它应该调用 reject 函数并传递失败的原因。
Promise 构造函数返回一个 Promise 对象，该对象具有以下几个方法：
then：用于处理 Promise 成功状态的回调函数。
catch：用于处理 Promise 失败状态的回调函数。
finally：无论 Promise 是成功还是失败，都会执行的回调函数。
下面是一个使用 Promise 构造函数创建 Promise 对象的例子：
当 Promise 被构造时，起始函数会被同步执行：
   */
  promiseCatch() {
    // 创建Promise对象，传入一个函数(起始函数)，该函数默认带2个(方法)参数(resolve、reject)，这两个参数对应回调起始函数任务的成功(回调到then)或者失败回调到catch。【可以都在then中弄2个回调函数，第一个对应success】
    const promise = new Promise((resolve, reject) => {
      console.log(new Date(), '开始网络请求')
      setTimeout(() => {
        let rd = Math.random()
        if (rd > 0.5) {
          resolve('成功返回了网络请求信息')
        } else {
          reject('404 资源不存在')
        }
        console.log(new Date(), '网络请求结束', rd)
      }, 1500);
    })
    // 用对象设置回调
    promise.then((success) => {
      console.log(new Date(), 'success', success)
    }).catch((fail) => {
      console.log(new Date(), 'fail', fail)
    }).finally((complete) => {
      console.log(new Date(), 'complete', complete)
    })
  },

  /**
   * Promise 类有 .then() .catch() 和 .finally() 三个方法，这三个方法的参数都是一个函数，.then() 可以将参数中的函数添加到当前 Promise 的正常执行序列，.catch() 则是设定 Promise 的异常处理序列，.finally() 是在 Promise 执行的最后一定会执行的序列。 .then() 传入的函数会按顺序依次执行，有任何异常都会直接跳到 catch 序列：
   * resolve() 中可以放置一个参数用于向下一个 then 传递一个值，then 中的函数也可以返回一个值传递给 then。但是，如果 then 中返回的是一个 Promise 对象，那么下一个 then 将相当于对这个返回的 Promise 进行操作，这一点从刚才的计时器的例子中可以看出来。
   * 
   * reject() 参数中一般会传递一个异常给之后的 catch 函数用于处理异常。
但是请注意以下两点：
resolve 和 reject 的作用域只有起始函数，不包括 then 以及其他序列；
resolve 和 reject 并不能够使起始函数停止运行，别忘了 return。

Thu Aug 29 2024 20:55:05 GMT+0800 (中国标准时间) "doubleThen 起始函数 111" ƒ () { [native code] } ƒ () { [native code] }
simple.js:136 Thu Aug 29 2024 20:55:05 GMT+0800 (中国标准时间) "doubleThen then1 " 222
simple.js:139 Thu Aug 29 2024 20:55:05 GMT+0800 (中国标准时间) "doubleThen then2 " 333
simple.js:152 444---444
simple.js:142 Thu Aug 29 2024 20:55:05 GMT+0800 (中国标准时间) "doubleThen then3 " undefined
simple.js:145 Thu Aug 29 2024 20:55:05 GMT+0800 (中国标准时间) "doubleThen catch" "error"
simple.js:147 
   */
  doubleThen() {
    new Promise((resolve, reject) => {
      console.log(new Date(), 'doubleThen 起始函数 111', resolve, reject)
      resolve(222)
      return
    }).then((res) => {
      console.log(new Date(), 'doubleThen then1 ', res)
      return 333
    }).then((res) => {
      console.log(new Date(), 'doubleThen then2 ', res)
      return this.then444() // 方法没有返回值时，即undefined，会把undefined传给下一个then
    }).then((res) => {
      console.log(new Date(), 'doubleThen then3 ', res)
      throw 'error'
      // reject('error from reject')  // resolve 和 reject 的作用域只有起始函数，不包括 then 以及其他序列；
    }).catch((fail) => {
      console.log(new Date(), 'doubleThen catch', fail)
    }).finally(() => {
      console.log(new Date(), 'doubleThen finally')
    })
  },

  then444() {
    console.log('444---444')
  },

  promiseFunction() {
    let msg = 'promiseFunction loadImage1'
    console.log(new Date(), 'promiseFunction 111', msg)
    this.loadImage(1000, msg).then(() => {
      return this.loadImage(2000, 'load second image')
    }).then(() => {
      this.loadImage(2000, 'load second image')
    }).catch((fail) => {
      console.log(new Date(), 'catch', fail) // Fri Aug 30 2024 09:50:26 GMT+0800 (中国标准时间) undefined
    }).finally(() => {
      console.log(new Date(), 'end')
    })
    console.log(new Date(), 'promiseFunction 222', msg)
  },
  /**
   * 
   * @param {*} delay 
   * @param {*} msg 
回答常见的问题（FAQ）
Q: then、catch 和 finally 序列能否顺序颠倒？

A: 可以，效果完全一样。但不建议这样做，最好按 then-catch-finally 的顺序编写程序。

Q: 除了 then 块以外，其它两种块能否多次使用？

A: 可以，finally 与 then 一样会按顺序执行，但是 catch 块只会执行第一个，除非 catch 块里有异常。所以最好只安排一个 catch 和 finally 块。

Q: then 块如何中断？

A: then 块默认会向下顺序执行，return 是不能中断的，可以通过 throw 来跳转至 catch 实现中断。

Q: 什么时候适合用 Promise 而不是传统回调函数？

A: 当需要多次顺序执行异步操作的时候，例如，如果想通过异步方法先后检测用户名和密码，需要先异步检测用户名，然后再异步检测密码的情况下就很适合 Promise。

Q: Promise 是一种将异步转换为同步的方法吗？

A: 完全不是。Promise 只不过是一种更良好的编程风格。

Q: 什么时候我们需要再写一个 then 而不是在当前的 then 接着编程？

A: 当你又需要调用一个异步任务的时候。
   */
  loadImage(delay, msg) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        if (msg.includes('second')) {
          console.log(new Date(), msg + " 图片加载失败 404")
          reject('404') // 一般建议加一个失败描述
        } else {
          console.log(new Date(), msg + " 图片加载成功")
          resolve()
        }
      }, delay);
    })
  },

  promiseAsyncFun() {
    // this.promiseAsyncFun() // 死循环 报错；Maximum call stack size exceeded
    this.asyncFun() // 死循环 报错；Maximum call stack size exceeded
  },

  /**
   异步函数 async function 
   中可以使用 await 指令，await 指令后必须跟着一个 Promise，异步函数会在这个 Promise 运行中暂停，直到其运行结束再继续运行。
   */
  async asyncFun() {
    // await 等待，与async 连用
    // Fri Aug 30 2024 10:29:26 GMT+0800 (中国标准时间) "promiseFunction 111" "promiseFunction loadImage1"
    // simple.js? [sm]:184 Fri Aug 30 2024 10:29:26 GMT+0800 (中国标准时间) "promiseFunction 222" "promiseFunction loadImage1"
    // simple.js? [sm]:222 Fri Aug 30 2024 10:29:27 GMT+0800 (中国标准时间) "promiseFunction loadImage1 图片加载成功"
    // simple.js? [sm]:219 Fri Aug 30 2024 10:29:29 GMT+0800 (中国标准时间) "load second image 图片加载失败 404"
    // simple.js? [sm]:180 Fri Aug 30 2024 10:29:29 GMT+0800 (中国标准时间) "catch" "404"
    // simple.js? [sm]:182 Fri Aug 30 2024 10:29:29 GMT+0800 (中国标准时间) "end"
    // simple.js? [sm]:240 Fri Aug 30 2024 10:29:31 GMT+0800 (中国标准时间) "asyncFun 111"
    // simple.js? [sm]:222 Fri Aug 30 2024 10:29:32 GMT+0800 (中国标准时间) "异步函数加载图片 图片加载成功"
    // simple.js? [sm]:242 Fri Aug 30 2024 10:29:32 GMT+0800 (中国标准时间) "asyncFun 222" 
    // 有上述日志可以看出await的不同，会等待耗时(promise)执行完
    console.log(new Date(), 'asyncFun 111')
    await this.loadImage(1000, '异步函数加载图片')
    console.log(new Date(), 'asyncFun 222')
    try {
      await this.loadImage(1000, '异步加载 second 张图片')
    } catch (error) {
      // 没有try catch 会抛出异常导致 asyncFun 333 不执行
      console.log(new Date(), 'asyncFun second 张图片 error ', error)
    }
    console.log(new Date(), 'asyncFun 333')
  },

  promiseAsyncFunCatch() {
    this.asyncFunCatch()
    this.asyncFunThen()
  },

  // reject
  async asyncFunCatch() {
    let a
    // try {
      console.log(new Date(), 'asyncFunCatch 111 a ', a)
      a = await new Promise((resolve, reject) => {
        setTimeout(() => {
          for (let index = 0; index < 2; index++) {
            if (index == 1) {
              reject('reject error') // 不会中断该起始函数正在运行的逻辑
              // throw 'throw error'// 中断该起始函数正在运行的逻辑
            }
            console.log(new Date(), 'asyncFunCatch---', index)
          }
        }, 1000)
      })
    // } catch (error) {
    //   console.log(new Date(), 'asyncFunCatch try catch', error)
    // }
    // Promise 内部中 reject后 不 try catch会直接抛出异常中断改方法内后续的逻辑
    console.log(new Date(), 'asyncFunCatch 222 a ', a)
    a.then((success) => {
      console.log(new Date(), 'asyncFunCatch a then ', success)
    })
  },
  // then
  async asyncFunThen() { //  Unexpected reserved word 'await'. (300:8); 方法内使用了await 必须要声明方法为async
    let a
    console.log(new Date(), 'asyncFunThen 111 a ', a)
    // 返回的是Promise的结果（resolve、reject）
    a = await new Promise((resolve, reject) => {
      setTimeout(() => {
        for (let index = 0; index < 3; index++) {
          if (index == 0) {
            resolve('目标元素') // 不会中断该起始函数正在运行的逻辑
          }
          console.log(new Date(), 'asyncFunThen---', index)
        }
      }, 1000)
    })

    console.log(new Date(), 'asyncFunThen 222 a ', a)
    a.then(()=>{
      console.log(new Date(), 'asyncFunThen then ', a)
    })
  },

  /**
   * 生命周期函数--监听页面隐藏
   */
  onHide() {

  },

  /**
   * 生命周期函数--监听页面卸载
   */
  onUnload() {

  },

  /**
   * 页面相关事件处理函数--监听用户下拉动作
   */
  onPullDownRefresh() {

  },

  /**
   * 页面上拉触底事件的处理函数
   */
  onReachBottom() {

  },

  /**
   * 用户点击右上角分享
   */
  onShareAppMessage() {

  }
})